{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 概述\n",
    "\n",
    "这篇文章分享 Git 分支设计规范，目的是提供给研发人员做参考。  \n",
    "规范是死的，人是活的，希望自己定的规范，不要被打脸。\n",
    "\n",
    "<br>\n",
    "\n",
    "在说 Git 分支规范之前，先说下在系统开发过程中常用的**环境**。\n",
    "\n",
    "|简称\t|全称| 作用\n",
    "|:---|:----|:-----|\n",
    "|DEV |Development environment| 用于开发者调试使用\n",
    "|FAT |Feature Acceptance Test environment|功能验收测试环境，用于测试环境下的软件测试者测试使用\n",
    "|UAT |User Acceptance Test environment|用户验收测试环境，用于生产环境下的软件测试者测试使用\n",
    "|PRO |Production environment|就是生产环境\n",
    "\n",
    "比如，项目域名为：`http://www.abc.com`，那么相关环境的域名可这样配置：\n",
    "\n",
    "- DEV 环境：本地配置虚拟域名即可\n",
    "- FAT 环境：`http://fat.abc.com`\n",
    "- UAT 环境：`http://uat.abc.com`\n",
    "- PRO 环境：`http://www.abc.com`\n",
    "\n",
    "<br>\n",
    "<br>\n",
    "\n",
    "接下来，针对不同的环境来设计**分支**。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 分支\n",
    "\n",
    "|分支\t  |名称 |\t环境\t|可访问\n",
    "|:----|:------|:-----|:-----|\n",
    "|master\t |主分支\t|PRO\t|是\n",
    "|release\t|预上线分支\t|UAT |\t是\n",
    "|hotfix\t |紧急修复分支\t|DEV|\t否\n",
    "|develop\t|测试分支\t|FAT\t|是\n",
    "|feature\t|需求开发分支|\tDEV|\t否\n",
    "\n",
    "## master 分支\n",
    "\n",
    "`master` 为主分支，用于部署到正式环境（PRO），一般由 `release` 或 `hotfix`  分支合并\n",
    "\n",
    "- **任何情况下不允许直接在 master 分支上修改代码。**\n",
    "- 分支上锁，只允许核心人员操作，不可随意推送。\n",
    "- 永远只存可以发布到生产环境的代码。\n",
    "\n",
    "\n",
    "## release 分支\n",
    "\n",
    "`release` 为预上线分支，用于部署到预上线环境（UAT），始终保持与 `master` 分支一致，一般由 `develop` 或 `hotfix` 分支合并，\n",
    "\n",
    "- 不建议直接在 `release` 分支上直接修改代码。\n",
    "- 该分支通过版本号命名。\n",
    "- 一次只能出现一个版本。\n",
    "- 如果在 `release `分支测试出问题，需要回归验证 `develop` 分支看否存在此问题。\n",
    "\n",
    "```sh\n",
    "// 从 dev 分支创建一个预发布分支\n",
    "$git checkout -b release-1.0\n",
    "// 合并到 master 主分支\n",
    "$git checkout master\n",
    "$git merge –no-ff release-1.0\n",
    "// 对合并生成的新节点,做一个标签.\n",
    "$git tag -a 1.0\n",
    "$git branch -d release-1.0\n",
    "```\n",
    "\n",
    "## hotfix 分支\n",
    "\n",
    "`hotfix` 为紧急修复分支，命名规则为 `hotfix-` 开头，当线上出现紧急问题需要马上修复时，需要基于 `release` 或 `master` 分支创建 `hotfix` 分支\n",
    "\n",
    "- 修复完成后，再合并到 `release` 或 `develop` 分支，一旦修复上线，便将其删除\n",
    "- 该分支根据需要修复的 BUG 命名\n",
    "- 允许同时出现多个 BUG 修复分支\n",
    "\n",
    "\n",
    "## develop 分支\n",
    "\n",
    "`develop` 为测试分支，用于部署到测试环境（FAT），始终保持最新完成以及 bug 修复后的代码，可根据需求大小程度确定是由 `feature` 分支合并，还是直接在上面开发。\n",
    "\n",
    "**一定是满足测试的代码才能往上面合并或提交。**\n",
    "\n",
    "## feature 分支\n",
    "\n",
    "`feature` 为需求开发分支，命名规则为 `feature-` 开头，**一旦该需求上线，便将其删除。**\n",
    "\n",
    "- 该分支根据需要做的功能命名。\n",
    "- 允许同时出现多个功能分支。\n",
    "- 功能开发结束并合并到`master`后，删除该分支。\n",
    "\n",
    "```sh\n",
    "// 从 dev 分支创建一个名为 feature-addNumber 的功能分支\n",
    "$git checkout -b feature-addNumber\n",
    "// 开发完成后合并到 dev 分支\n",
    "$git checkout dev\n",
    "$git merge --no-ff feature-addNumber\n",
    "// 删除功能分支\n",
    "$git branch -d feature-addNumber\n",
    "```\n",
    "\n",
    "<br>\n",
    "这么说可能有点不容易理解，接下来举几个开发场景。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 开发场景\n",
    "\n",
    "## 新需求加入\n",
    "\n",
    "有一个订单管理的新需求需要开发，首先要创建一个 `feature-order` 分支，问题来了，该分支是基于哪个分支创建？\n",
    "\n",
    "如果 存在 未测试完毕的需求，就基于 `master` 创建。\n",
    "\n",
    "如果 不存在 未测试完毕的需求，就基于 `develop` 创建。\n",
    "\n",
    "1. 需求在 `feature-order` 分支开发完毕，准备提测时，要先确定 `develop` 不存在未测试完毕的需求，这时研发人员才能将将代码合并到 `develop` （测试环境）供测试人员测试。\n",
    "\n",
    "2. 测试人员在 `develop` （测试环境） 测试通过后，研发人员再将代码发布到 `release` （预上线环境）供测试人员测试。\n",
    "\n",
    "3. 测试人员在 `release` （预上线环境）测试通过后，研发人员再将代码发布到 `master` （正式环境）供测试人员测试。\n",
    "\n",
    "4. 测试人员在 `master` (正式环境) 测试通过后，研发人员需要删除 `feature-order` 分支。\n",
    "\n",
    "## 普通迭代\n",
    "\n",
    "有一个订单管理的迭代需求，如果开发工时 < 1d，直接在 `develop` 开发，如果开发工时 > 1d，那就需要创建分支，在分支上开发。\n",
    "\n",
    "开发后的提测上线流程 与 新需求加入的流程一致。\n",
    "\n",
    "## 修复测试环境 Bug\n",
    "\n",
    "在 `develop` 测试出现了Bug，如果修复工时 < 2h，直接在 `develop` 修复，如果修复工时 > 2h，需要在分支上修复。\n",
    "\n",
    "修复后的提测上线流程 与 新需求加入的流程一致。\n",
    "\n",
    "```sh\n",
    "// 从 dev 分支创建一个名为 fixbug-001 的 bug 分支\n",
    "$git checkout -b fixbug-001\n",
    "// bug 修复完成后合并到 dev 分支\n",
    "$git checkout dev\n",
    "$git merge --no-ff fixbug-001\n",
    "// 删除 bug 分支\n",
    "$git branch -d fixbug-001\n",
    "```\n",
    "\n",
    "## 修改预上线环境 Bug\n",
    "\n",
    "在 `release` 测试出现了Bug，首先要回归下 `develop` 分支是否同样存在这个问题。\n",
    "\n",
    "如果存在，修复流程 与 修复测试环境 Bug流程一致。\n",
    "\n",
    "如果不存在，这种可能性比较少，大部分是数据兼容问题，环境配置问题等。\n",
    "\n",
    "## 修改正式环境 Bug\n",
    "\n",
    "在 master 测试出现了Bug，首先要回归下 release 和 develop 分支是否同样存在这个问题。\n",
    "\n",
    "如果存在，修复流程 与 修复测试环境 Bug流程一致。\n",
    "\n",
    "如果不存在，这种可能性也比较少，大部分是数据兼容问题，环境配置问题等。\n",
    "\n",
    "## 紧急修复正式环境 Bug\n",
    "\n",
    "需求在测试环节未测试出 Bug，上线运行一段时候后出现了 Bug，需要紧急修复的。\n",
    "\n",
    "我个人理解紧急修复的意思是没时间验证测试环境了，但还是建议验证下预上线环境。\n",
    "\n",
    "- 如果 `release` 分支存在未测试完毕的需求，就基于` master` 创建 `hotfix-xxx` 分支，修复完毕后发布到 master 验证，验证完毕后，将 master 代码合并到 `release` 和 `develop` 分支，同时删掉 `hotfix-xxx` 分支。\n",
    "\n",
    "- 如果 `release` 分支不存在未测试完毕的需求，但 `develop` 分支存在未测试完毕的需求，就基于 `release` 创建 `hotfix-xxx` 分支，修复完毕后发布到 `release` 验证，后面流程与上线流程一致，验证完毕后，将 `master` 代码合并到 `develop` 分支，同时删掉 `hotfix-xxx` 分支。\n",
    "\n",
    "- 如果 `release` 和 `develop` 分支都不存在未测试完毕的需求， 就直接在 `develop` 分支上修复完毕后，发布到 `release` 验证，后面流程与上线流程一致。\n",
    "\n",
    "## 并行提测\n",
    "\n",
    "在一个项目中并行开发了两个需求，并行提测，但是上线日期不同。\n",
    "\n",
    "我能想到的两种方案：\n",
    "\n",
    "- 再部署一套可供测试人员测试的分支\n",
    "- 使用 git cherry-pick “挑拣”提交\n",
    "\n",
    "对于并行提测，你有好的方案吗？欢迎留言。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Commit 日志规范\n",
    "\n",
    "提交信息一定要认真填写！\n",
    "\n",
    "建议参考规范：`<type>(scope)：<subject>`\n",
    "\n",
    "比如：<U>fix(首页模块)：修复弹窗 JS Bug</U>。\n",
    "\n",
    "`type` 表示 动作类型，可分为：\n",
    "\n",
    "- fix：修复 xxx Bug\n",
    "- feat：新增 xxx 功能\n",
    "- test：调试 xxx 功能\n",
    "- style：变更 xxx 代码格式或注释\n",
    "- docs：变更 xxx 文档\n",
    "- refactor：重构 xxx 功能或方法\n",
    "- scope 表示 影响范围，可分为：模块、类库、方法等。\n",
    "\n",
    "`subject` 表示 简短描述，最好不要超过 60 个字，如果有相关 Bug 的 Jira 号，建议在描述中加上。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 示例操作流程\n",
    "\n",
    "```sh\n",
    "// 从远程仓库克隆项目 dev 分支 \n",
    "$git clone -b dev http://git.ts-it.cn/BornToRain/testGit.git\n",
    "// 进入项目目录\n",
    "$cd testGit\n",
    "// 从 dev 分支创建功能分支\n",
    "$git checkout -b feature-helloworld\n",
    "// 创建一个${姓名}.java\n",
    "$vim xxx.java\n",
    "// 加入 Git\n",
    "$git add xxx.java\n",
    "// 提交到本地仓库 \n",
    "$git commit -m \"新增(xxx) 功能已完成\"\n",
    "// 切换回 dev 分支\n",
    "$git checkout dev\n",
    "// 将功能分支实现的功能合并到 dev 分支上\n",
    "$git merge --no-ff feature-helloworld\n",
    "// 推送本地 dev 分支内容到远程仓库\n",
    "$git push origin dev\n",
    "// 测试无误后删除本地功能分支\n",
    "$git branch -d feature-helloworld\n",
    "```"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "172.6px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
